//
//  WrappedJsval.m
//  iCodeMacRun
//
//  Created by Andrew Barry on 18/03/09.
//  Copyright 2009 Barry Computer Services. All rights reserved.
//

#import "WrappedJsval.h"

static jsvalue autoboxNSObject(jscontext *_cx, NSObject *p) {
	if ([p isKindOfClass:[NSString class]]) {
		return jsstrFromNSString(_cx, (NSString *) p);
	} else if (p == [NSNull null]) {
		return JSVAL_NULL;
	} else if ([p isKindOfClass: [NSNumber class]]) {
		
		NSNumber *nsv = (NSNumber *) p;
		const char *ct = [nsv objCType];
		if (!strcmp(ct, "c"))
			return jsnumi(_cx, [nsv intValue]);
		else
			return jsnum(_cx, [nsv doubleValue]);
	} else if ([p isKindOfClass: [WrappedJsval class]]) {
		WrappedJsval *w = (WrappedJsval *) p;
		return w->val;
	} else {
		return JSVAL_VOID;
	}
}

@implementation WrappedJsval
+(WrappedJsval *) wrap: (jsvalue) v withContext: (jscontext *) cx {
	return [[[WrappedJsval alloc] initWithVal:v withContext:cx] autorelease];
}

-(id)initWithVal: (jsvalue) v  withContext: (jscontext *) cx {
	self = [self init];
	jsaddroot(cx, &val);
	val = v;
	_cx = cx;
	return self;
}

-(void)dealloc {
	jsremoveroot(_cx, &val);
	[super dealloc];
}

- (id)copyWithZone:(NSZone *)zone
{
    return [self retain];
}

-(WrappedJsval *) get: (NSString *) field {
	jsvalue v = jsget(_cx, val, 0, [field UTF8String]);
	if (v) {
		return [WrappedJsval wrap: v withContext: _cx];
	} else
		return nil;
}

-(void) setObject: (NSObject *) v forKey: (NSString *) field {
	jsvalue jsv = autoboxNSObject(_cx, v);
	jsset(_cx, val, 0, [field UTF8String], jsv);
}

-(WrappedJsval *) invoke: (NSString *) name args: (NSArray *) args {
	WrappedJsval *fn = [self get: name];
	int count = [args count];
	jsvalue *invokeParams = malloc(sizeof(jsvalue) * count);
	memset(invokeParams, 0, sizeof(jsvalue) * count);
	jsaddrootmulti(_cx, invokeParams, count);
	
	for (int idx = 0; idx < count ;idx++) {
		NSObject *p = [args objectAtIndex:idx];
		if ([p isKindOfClass:[NSString class]]) {
			invokeParams[idx] = jsstrFromNSString(_cx, (NSString *) p);
		} else if (p == [NSNull null]) {
			invokeParams[idx] = JSVAL_NULL;
		} else if ([p isKindOfClass: [NSNumber class]]) {
			
			NSNumber *nsv = (NSNumber *) p;
			const char *ct = [nsv objCType];
			if (!strcmp(ct, "c"))
				invokeParams[idx] = jsnumi(_cx, [nsv intValue]);
			else
				invokeParams[idx] = jsnum(_cx, [nsv doubleValue]);
		} else if ([p isKindOfClass: [WrappedJsval class]]) {
			WrappedJsval *w = (WrappedJsval *) p;
			invokeParams[idx] = w->val;
		} else {
			invokeParams[idx] = JSVAL_VOID;
		}
	}

	jsvalue result = jsinvoke(_cx, fn->val, self->val, invokeParams, count);
	jsremoveroot(_cx, invokeParams);
	free(invokeParams);
	if (result)
		return [WrappedJsval wrap:result withContext:_cx];
	else
		return nil;
}

@end
